package com.svarlet.fileSearch
{
	import flash.events.EventDispatcher;
	import flash.events.FileListEvent;
	import flash.events.IOErrorEvent;
	import flash.filesystem.File;
	
	[Event(name="searchEnd", type="com.svarlet.fileSearch.FileSearchEvent")]
	[Event(name="fileFound", type="com.svarlet.fileSearch.FileSearchEvent")]
	[Event(name="ioError", type="flash.events.IOErrorEvent")]
	
	/**
	 * This class has been made to parse a file hierarchy.
	 * 
	 * The scan is done synchronously from the root parameter of the
	 * <code>search()</code> method. For each file or directory found, a
	 * <code>FileSearchEvent.FILE_FOUND</code> is dispatched. When search ends, another
	 * <code>FileSearchEvent.SEARCH_END</code> is dispatched.
	 * 
	 * Example:
	 * <pre>
	 * 	public class MediaScanner
	 * 	{
	 * 		public var fs:FileSearcher;
	 * 		public var supportedExtensions:Array;
	 * 
	 * 		public function MediaScanner()
	 * 		{
	 * 			fs = new FileSearcher();
	 * 			supportedExtensions =  = [ "mp3", "flv" ];
	 * 		}
	 * 
	 * 		protected function fileFoundHandler(event:FileSearchEvent):void
	 * 		{
	 * 			for each (var ext:String in supportedExtensions) {
	 * 				if (event.file.extension == ext) {
	 * 					trace(event.file.nativePath);
	 * 				}
	 * 			}
	 * 		}
	 * 
	 * 		protected function searchEndHandler(event:FileSearchEvent):void
	 * 		{
	 * 			trace("Finished !");
	 * 		}
	 * 	}
	 * 
	 * </pre>
	 * 
	 * @author Sébastien Varlet
	 */
	public class FileSearcher extends EventDispatcher
	{
		private var _doEnd:Boolean;
		private var _toScan:Vector.<File>;
		
		/**
		 * Constructor
		 */
		public function FileSearcher()
		{
			_doEnd = false;
			_toScan = new Vector.<File>();
		}
		
		/**
		 * Starts a synchronous iterative search from a root file or directory. 
		 */
		public function search(root:File):void
		{
			if (!root)
			{
				return;
			}
			
			dispatchEvent(new FileSearchEvent(FileSearchEvent.FILE_FOUND, root));
			
			if (root.isDirectory)
			{
				root.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				root.addEventListener(FileListEvent.DIRECTORY_LISTING, directoryListingHandler);
				root.getDirectoryListingAsync();
			}
		}
		
		/**
		 * Redispatch IOErrorEvent dispatched by a search process when it
		 * encounters some permissions problems with a file or a directory.
		 */
		protected function ioErrorHandler(event:IOErrorEvent):void
		{
			var file:File = File(event.target);
			
			file.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			file.removeEventListener(FileListEvent.DIRECTORY_LISTING, directoryListingHandler);
			dispatchEvent(event);
		}
		
		/**
		 * Scan the content of a directory by dispatching
		 * <code>FileSearchEvent.FILE_FOUND</code> events for regular files and
		 * directories.
		 * At the end, restart the search process on remaining directories.
		 */
		protected function directoryListingHandler(event:FileListEvent):void
		{
			var file:File = File(event.target);
			
			file.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			file.removeEventListener(FileListEvent.DIRECTORY_LISTING, directoryListingHandler);
			
			if (_doEnd)
			{
				_toScan.splice(0, _toScan.length);
				dispatchEvent(new FileSearchEvent(FileSearchEvent.SEARCH_END));
			}
			else
			{
				for each (var f:File in event.files)
				{
					if (!f.isDirectory)
					{
						dispatchEvent(new FileSearchEvent(FileSearchEvent.FILE_FOUND, f));
					}
					else
					{
						dispatchEvent(new FileSearchEvent(FileSearchEvent.DIR_FOUND, f));
						//_toScan.push(f);
					}
				}
				
				if (_toScan.length == 0)
				{
					dispatchEvent(new FileSearchEvent(FileSearchEvent.SEARCH_END));
				}
				else
				{
					search(_toScan.shift());
				}
			}
		}
		
		/**
		 * Terminate the current search.
		 */
		public function end():void
		{
			_doEnd = true;
		}
	}
}